package com.fitns.user.conf;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fitns.user.exception.CommMsgCode;
import com.fitns.user.exception.ServiceException;
import com.fitns.user.model.core.Field;
import com.xiaoleilu.hutool.lang.Validator;
import com.xiaoleilu.hutool.util.CollectionUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 统一返回包装 Created by lenovo on 2017/7/29.
 */
@Data
@AllArgsConstructor
public class ZhhwResp<T> implements Serializable {

	private static final long serialVersionUID = 4726403067280173515L;

	private int code = 200;
	private String message = "";
	private String resultCode ="SUCESS";
	private T data;

	public ZhhwResp() {
		this.includeNative(ZhhwResp.class, "code", "data", "message", "resultCode");
	}

	public ZhhwResp(T data) {
		this();
		this.data = data;
	}

	public ZhhwResp(String message) {
		this();
		this.message = message;
	}

	public static <E> ZhhwResp<E> createSuccess(E data) {
		return new ZhhwResp<E>(data);
	}

	/**
	 * 指定某个类在序列化时包含的属性，如果指定了，则这个类在序列化时，只会序列化指定的属性 如果重复指定,则后指定的回覆盖之前的
	 * 使用方法，例如返回的Documents对象只返回,documentId,creationDate,url 三个字段
	 * ResponseResult.createSuccess(document).include(Documents.class,
	 * "documentId","creationDate","url")
	 *
	 * @param clazz
	 *            要指定的类型
	 * @param
	 *
	 * @return
	 */
	public ZhhwResp<T> include(Class<?> clazz, Field... fields) {
		String[] arr = this.convertFieldsToArray(fields);
		return this.include(clazz, arr);
	}

	public ZhhwResp<T> include(Class<?> clazz, String... properties) {
		if (clazz.equals(ZhhwResp.class)) {
			throw ServiceException.create(CommMsgCode.OTHER_ERROR);
		}
		addToMap(includeMap, clazz, properties);
		return this;
	}

	private void addToMap(Map<Class<?>, Set<String>> map, Class<?> clazz, Field... fields) {
		String[] arr = this.convertFieldsToArray(fields);
		this.addToMap(map, clazz, arr);
	}

	private void addToMap(Map<Class<?>, Set<String>> map, Class<?> clazz, String... properties) {
		Set<String> set = map.get(clazz);
		if (Validator.isNull(set)) {
			set = new HashSet<>();
		}
		set.addAll(this.getSetFromProperties(properties));
		map.put(clazz, set);
	}

	/**
	 * 指定某个类在序列化时不包含的属性，如果指定了不包含的属性，则这个类在序列化时，不会序列化指定的属性
	 * 如果一个类包含了指定了序列化时包含的属性(includeMap)，则指定不包含的属性不会生效，
	 * 
	 * 使用方法，例如返回的Documents对象不需要返回creationDate
	 * ResponseResult.createSuccess(document).exclude(Documents.class,
	 * "creationDate")
	 * 
	 * @param clazz
	 *            要指定的类型
	 * @param
	 *
	 * @return
	 */

	public ZhhwResp<T> exclude(Class<?> clazz, Field... fields) {
		String[] arr = convertFieldsToArray(fields);
		return exclude(clazz, arr);
	}
	
	public ZhhwResp<T> exclude(Class<?> clazz, String... properties) {
		if (clazz.equals(ZhhwResp.class)) {
			throw ServiceException.create(CommMsgCode.IO_ERROR);
		}
		addToMap(excludeMap, clazz, properties);
		return this;
	}

	private String[] convertFieldsToArray(Field... fields) {
		List<String> listFields = CollectionUtil.newArrayList(fields).parallelStream().map(Field::getFieldName)
				.collect(Collectors.toList());
		String[] arr = new String[listFields.size()];
		listFields.toArray(arr);
		return arr;
	}

	private void includeNative(Class<?> clazz, String... properties) {
		addToMap(includeMap, clazz, properties);
	//	addToMap(excludeMap, BaseEo.class, "isDelete");
	//	addToMap(excludeMap, BaseEo.class, Fields.OPERATOR_ID,Fields.MODIFY_DATE);
	}

	private Set<String> getSetFromProperties(String... properties) {
		Set<String> set = new HashSet<String>();
		for (String property : properties) {
			if (!StringUtils.isBlank(property)) {
				set.add(property.toUpperCase());
			}
		}
		return set;
	}

	public Map<Class<?>, Set<String>> getIncludeMap() {
		return this.includeMap;
	}

	public Map<Class<?>, Set<String>> getExcludeMap() {
		return this.excludeMap;
	}

	@JsonIgnore
	private final Map<Class<?>, Set<String>> includeMap = new HashMap<Class<?>, Set<String>>();
	@JsonIgnore
	private final Map<Class<?>, Set<String>> excludeMap = new HashMap<Class<?>, Set<String>>();

}
